/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package genetic;

import Utilities.Tradutor;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import org.neuroph.core.learning.DataSet;
import org.uncommons.maths.random.MersenneTwisterRNG;
import org.uncommons.maths.random.Probability;
import org.uncommons.watchmaker.framework.CandidateFactory;
import org.uncommons.watchmaker.framework.EvolutionEngine;
import org.uncommons.watchmaker.framework.EvolutionObserver;
import org.uncommons.watchmaker.framework.EvolutionaryOperator;
import org.uncommons.watchmaker.framework.FitnessEvaluator;
import org.uncommons.watchmaker.framework.GenerationalEvolutionEngine;
import org.uncommons.watchmaker.framework.PopulationData;
import org.uncommons.watchmaker.framework.SelectionStrategy;
import org.uncommons.watchmaker.framework.factories.StringFactory;
import org.uncommons.watchmaker.framework.operators.EvolutionPipeline;
import org.uncommons.watchmaker.framework.operators.StringCrossover;
import org.uncommons.watchmaker.framework.operators.StringMutation;
import org.uncommons.watchmaker.framework.selection.RouletteWheelSelection;
import org.uncommons.watchmaker.framework.termination.GenerationCount;

/**
 *
 * @author Celso
 */
public class GeneticAlgorithm {

    List<EvolutionaryOperator<String>> operators;
    EvolutionaryOperator<String> pipeline;
    FitnessEvaluator<String> fitnessEvaluator;
    SelectionStrategy<Object> selection;
    Random rng;
    EvolutionEngine<String> engine;
    String result;
    CandidateFactory<String> factory;
    Tradutor tradutor;

    public GeneticAlgorithm(
            Integer candidateSize,
            Double crossoverRate,
            Double mutationRate,
            DataSet trainingSet,
            DataSet testingSet) {

        char[] alphabet = {'0', '1'};
        factory = new StringFactory(alphabet, candidateSize);
        operators = new LinkedList<>();
        operators.add(new StringMutation(alphabet, new Probability(mutationRate)));
        operators.add(new StringCrossover(2, new Probability(crossoverRate)));
        pipeline = new EvolutionPipeline<>(operators);
        this.tradutor = new Tradutor();
        fitnessEvaluator = new NeuralNetworkEvaluator(tradutor, trainingSet, testingSet);
        selection = new RouletteWheelSelection();
        rng = new MersenneTwisterRNG();
        engine = new GenerationalEvolutionEngine<>(factory,
                pipeline,
                fitnessEvaluator,
                selection,
                rng);
       this.result = null;

    }

    public void evolve(Integer populationSize, Integer elitism, Integer generations) {
         result = engine.evolve(populationSize, elitism, new GenerationCount(generations));
    }

    public String getResult() {
        return result;
    }

    public void observeEvolution() {
        engine.addEvolutionObserver(new EvolutionObserver<String>() {
            @Override
            public void populationUpdate(PopulationData<? extends String> data) {
                System.out.println("\n Generation:" + data.getGenerationNumber());
                String bestCandidate = data.getBestCandidate();
                tradutor.translate(bestCandidate);
                System.out.println("Transfer Function :" + tradutor.getTransferFunctionType());
                System.out.println("Hidden Neurons :" + tradutor.getHiddenNeurons());
                System.out.println("Learn Rate:" + tradutor.getLearnRate());
                System.out.println("Momentum:" + tradutor.getMomentum());
                System.out.println("Fitnes: " + data.getBestCandidateFitness());
                System.out.println("");

            }
        });
    }

}
